package com.spikything.utils {
	
	import flash.display.*;
	import flash.events.*;
	import flash.geom.*;
	import flash.media.SoundChannel;
	import flash.media.SoundTransform;
	import flash.utils.*;
	
	public class Utils {
		
		public static function addSprite (where:Sprite, what:Sprite, x:Number = 0, y:Number = 0, alpha:Number = 1, visible:Boolean=true, initObj:Object=null):Sprite {
			var sprite:Sprite = where.addChild(what) as Sprite;
			sprite.x = x;
			sprite.y = y;
			sprite.alpha = alpha;
			sprite.visible = visible;
			for (var i:String in initObj) sprite[i] = initObj[i];
			return sprite;
		}
		
		// Use a regular expression to validate an email address
		public static function validateEmail (email:String):Boolean {
			var emailExpression:RegExp = /^[a-z][\w.-]+@\w[\w.-]+\.[\w.-]*[a-z][a-z]$/i;
			return emailExpression.test(email);
		}
		
		// Turns seconds into minutes and seconds, e.g. 90 -> 1:30
		public static function formatTime (time:Number):String {
			var minutes:String = ""+Math.floor(time/60);
			var seconds:String = ""+Math.floor(time%60);
			while (seconds.length<2) seconds = "0"+seconds;
			return minutes+":"+seconds;
		}
		
		//
		public static function removeChild(obj:DisplayObjectContainer, child:DisplayObject):void {
			if ((obj == null) || (child == null) || (child.parent == null) || (child.parent != obj)) return;
			obj.removeChild(child);
			if (obj.stage != null) obj.stage.focus = obj.stage;
		}
		
		//
		public static function swapChildren(obj:Sprite, child1:DisplayObject, child2:DisplayObject):void {
			if ((child1 == null) || (child2 == null) || (!obj.contains(child1)) || (!obj.contains(child2))) return;
			obj.swapChildren(child1, child2);
		}
		
		//
		public static function swapToTop(obj:Sprite, top:DisplayObject, btm:DisplayObject):void {
			if ((obj == null) || (top == null) || (top.parent == null) || (top.parent != obj)) return;
			if (                 (btm == null) || (btm.parent == null) || (btm.parent != obj)) return;
			if (obj.getChildIndex(btm) > obj.getChildIndex(top)) obj.swapChildren(top, btm);
		}
		
		//
		public static function stringEndsWith(str:String, substr:String):Boolean {
			return (str.substr(0-substr.length) == substr);
		}
		
		//
		public static function getBitmapData(mc:DisplayObject):BitmapData {
			var bd:BitmapData = new BitmapData(mc.width, mc.height, true, 0);
			bd.draw(mc);
			return bd;
		}
		
		//
		public static function isParentOrSelf(parent:DisplayObject, child:InteractiveObject):Boolean {
			if (child == parent) return true;
			var c:DisplayObjectContainer = child.parent;
			while (c != null) {
				if (parent == c) {
					return true;
				}
				c = c.parent;
			}
			return false
		}
		
		//
		public static function swapOutWithBitmap(mc:Sprite, useScale:Boolean = true, smoothing:Boolean = false):void {
			
			var oldQuality:String;
			oldQuality = StageQuality.HIGH;
			if (mc.stage) oldQuality = mc.stage.quality;
			//mc.stage.quality = StageQuality.BEST;
			
			var xs:Number = mc.scaleX;
			var ys:Number = mc.scaleY;
			var w:Number = mc.width;
			var h:Number = mc.height;
			if (useScale) {
				w /= xs;
				h /= ys;
			}
			var x:Number = mc.x;
			var y:Number = mc.y;
			var b:Rectangle = mc.getBounds(mc.parent);
			if ((w>2880)||(h>2880)) return;
			var bd:BitmapData = new BitmapData(w+2, h+2, true, 0);
			var matrix:Matrix = new Matrix();
			matrix.translate(-b.x, b.y);
			if (!useScale) matrix.scale(xs,ys);
			matrix.translate(2,2);
			bd.draw(mc, matrix);
			var newmc:Sprite = new Sprite();
			if (useScale) {
				newmc.scaleX = xs;
				newmc.scaleY = ys;
			}
			newmc.x = Math.ceil(x+(b.x*(xs)));
			newmc.y = Math.ceil(y-(b.y*(ys)));
			newmc.graphics.beginBitmapFill(bd, null, false, smoothing);
			newmc.graphics.drawRect(0, 0, bd.width, bd.height);
			if (mc.parent && mc.stage) {
				mc.parent.addChild(newmc);
				mc.parent.swapChildren(newmc, mc);
				mc.stage.quality = oldQuality;
				mc.parent.removeChild(mc);
			}
		}
		
		//
		public static function setVolume(sndChan:SoundChannel, vol:Number):void {
			var st:SoundTransform = sndChan.soundTransform
			st.volume = vol;
			sndChan.soundTransform = st;
		}
		
		//
		public static function tint(obj:DisplayObject, c:uint):void {
			var ct:ColorTransform = obj.transform.colorTransform;
			ct.color = c;
			obj.transform.colorTransform = ct;
		}
		
		//
		public static function untint(obj:DisplayObject):void {
			obj.transform.colorTransform = new ColorTransform();
		}
		
		//
		public static function tween(a:Number, b:Number, factor:Number):Number {
			return (a * factor) + (b * (1-factor));
		}
		
		//
		public static function pixelSnap(obj:Sprite):void {
			if (obj.parent == null) return;
			var p:Point = obj.localToGlobal(new Point(obj.x, obj.y));
			p.x = Math.round(p.x);
			p.y = Math.round(p.y);
			var q:Point = obj.globalToLocal(p);
			obj.x = q.x;
			obj.y = q.y;
		}
		
		//
		public static function interpolateColour(a:uint, b:uint, p:Number):uint {
			var q:Number = 1-p;
			var ar:uint = a>>16;
			var ag:uint = (a & 0x00FF00)>>8;
			var ab:uint = (a & 0x0000FF);
			var br:uint = b>>16;
			var bg:uint = (b & 0x00FF00)>>8;
			var bb:uint = (b & 0x0000FF);
			return (((ar*p)+(br*q))<<16) + (((ag*p)+(bg*q))<<8) + ((ab*p)+(bb*q));
		}
		
		//
		private static function renderTriTo(mc:Sprite, texture:BitmapData, points:Array, tl:Boolean):void {
			var m:Matrix;
			var sx:Number;
			var sy:Number;
			if (tl) { // There's probably a superior bodge here
				m = new Matrix();
				sx = (points[1].x - points[0].x)/texture.width
				sy = (points[2].y - points[0].y)/texture.height
				m.scale(sx,sy);
				m.b = (points[1].y - points[0].y)/texture.width;
				m.c = (points[2].x - points[0].x)/texture.height;
				m.translate(points[0].x, points[0].y);
			} else {
				m = new Matrix();
				sx = (points[1].x - points[2].x)/texture.width
				sy = (points[1].y - points[0].y)/texture.height
				m.scale(sx,sy);
				m.b = (points[1].y - points[2].y)/texture.width;
				m.c = (points[1].x - points[0].x)/texture.height;
				m.translate(points[0].x - (points[1].x - points[2].x), points[0].y - (points[1].y - points[2].y));
			}
			mc.graphics.beginBitmapFill(texture,m,false,true);
			mc.graphics.moveTo(points[0].x, points[0].y);
			mc.graphics.lineTo(points[1].x, points[1].y);
			mc.graphics.lineTo(points[2].x, points[2].y);
			mc.graphics.lineTo(points[0].x, points[0].y);
			mc.graphics.endFill();
		}
		
		//
		public static function renderTo(mc:Sprite, texture:BitmapData, points:Array):void {
			renderTriTo(mc, texture, [points[0], points[1], points[3]], true);
			renderTriTo(mc, texture, [points[1], points[2], points[3]], false);
		}
		
		//
		public static function searchMatches(search:String, title:String, keywords:Array):Boolean {
			var searchWords:Array = search.split(" ");
			for each (var sw:String in searchWords) {
				if (!searchWordMatches(sw, title, keywords)) return false;
			}
			return true;
		}
		
		//
		public static function searchWordMatches(searchWord:String, title:String, keywords:Array):Boolean {
			searchWord = searchWord.toLowerCase();
			var titleWords:Array = title.toLowerCase().split(" ");
			for each (var tw:String in titleWords) {
				if (searchWord == tw.substr(0, searchWord.length)) return true;
			}
			for each (var kw:String in keywords) {
				if (searchWord == kw.substr(0, searchWord.length)) return true;
			}
			return false;
		}
		
		//
		public static function hasUnion(a:Array, b:Array):Boolean {
			for each (var aa:* in a) {
				for each (var bb:* in b) {
					if (aa == bb) return true;
				}
			}
			return false;
		}
		
		//
		public static function fitInside(obj:DisplayObject, w:Number, h:Number):void {
			if (obj.width / obj.height * h < w) { // Wide container, narrow thing
				obj.width = obj.width / obj.height * h;
				obj.height = h;
			} else { // Narrow container, wide thing
				obj.height = obj.height / obj.width * w;
				obj.width = w;
			}
			obj.x = Math.round((w - obj.width) /2);
			obj.y = Math.round((h - obj.height) /2);
		}
		
		//
		public static function layout(align:String, staticCoord:Number, alignCoord:Number, gutter:Number, items:Array, expandItem:int = -1, expandSize:Number = -1):Number {
			// TODO allow V align too
			var i:Number = alignCoord;
			for each (var item:* in items) {
				var size:Number = layoutItem(align, staticCoord, i, item);
				if (align==StageAlign.LEFT) {
					i += size + gutter;
				} else if (align==StageAlign.RIGHT) {
					i -= size + gutter;
				}
			}
			if (expandItem == -1) return i;
			var shunt:Number = (expandSize - i);
			items[expandItem].width += shunt;
			for (var shuntIndex:int=expandItem+1; shuntIndex<items.length; shuntIndex++) {
				if (items[shuntIndex] is Array) {
					for each(var shuntItem:DisplayObject in items[shuntIndex]) shuntItem.x += shunt;
				} else {
					items[shuntIndex].x += shunt;
				}
			}
			return expandSize;
		}
		
		//
		private static function layoutItem(align:String, staticCoord:Number, alignCoord:Number, item:*):Number {
			// TODO allow V align too
			var subitem:DisplayObject;
			var maxwidth:Number = 0;
			if (align==StageAlign.LEFT) {
				if (item is Array) {
					for each(subitem in item) {
						subitem.x = alignCoord;
						subitem.y = staticCoord;
						if (subitem.width > maxwidth) maxwidth = subitem.width;
					}
					return maxwidth;
				} else {
					item.x = alignCoord;
					item.y = staticCoord;
					return item.width;
				}
				
			} else if (align==StageAlign.RIGHT) {
				if (item is Array) {
					for each(subitem in item) {
						subitem.x = alignCoord - subitem.width;
						subitem.y = staticCoord;
						if (subitem.width > maxwidth) maxwidth = subitem.width;
					}
					return maxwidth;
				} else {
					item.x = alignCoord - item.width;
					item.y = staticCoord;
					return item.width;
				}
				
			}
			return 10; // err not really
		}
	}
	
}